"""
市场监控器

提供实时市场数据监控、市场状态跟踪、事件检测等功能。
"""

import asyncio
import logging
from datetime import datetime, timedelta
from decimal import Decimal
from enum import Enum
from typing import Dict, List, Optional, Callable, Any, Set
from dataclasses import dataclass, field
import json


class MarketStatus(Enum):
    """市场状态"""
    CLOSED = "closed"           # 闭市
    PRE_OPEN = "pre_open"      # 开盘前
    OPEN = "open"              # 开盘
    BREAK = "break"            # 休市
    PRE_CLOSE = "pre_close"    # 收盘前
    AFTER_HOURS = "after_hours" # 盘后


class MarketEvent(Enum):
    """市场事件类型"""
    PRICE_CHANGE = "price_change"           # 价格变动
    VOLUME_SPIKE = "volume_spike"           # 成交量异常
    VOLATILITY_SPIKE = "volatility_spike"   # 波动率异常
    CIRCUIT_BREAKER = "circuit_breaker"     # 熔断
    TRADING_HALT = "trading_halt"           # 停牌
    NEWS_EVENT = "news_event"               # 新闻事件
    TECHNICAL_SIGNAL = "technical_signal"   # 技术信号


@dataclass
class MarketData:
    """市场数据"""
    symbol: str
    timestamp: datetime
    price: Decimal
    volume: int
    bid_price: Optional[Decimal] = None
    ask_price: Optional[Decimal] = None
    bid_size: Optional[int] = None
    ask_size: Optional[int] = None
    high: Optional[Decimal] = None
    low: Optional[Decimal] = None
    open_price: Optional[Decimal] = None
    prev_close: Optional[Decimal] = None
    turnover: Optional[Decimal] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            'symbol': self.symbol,
            'timestamp': self.timestamp.isoformat(),
            'price': str(self.price),
            'volume': self.volume,
            'bid_price': str(self.bid_price) if self.bid_price else None,
            'ask_price': str(self.ask_price) if self.ask_price else None,
            'bid_size': self.bid_size,
            'ask_size': self.ask_size,
            'high': str(self.high) if self.high else None,
            'low': str(self.low) if self.low else None,
            'open_price': str(self.open_price) if self.open_price else None,
            'prev_close': str(self.prev_close) if self.prev_close else None,
            'turnover': str(self.turnover) if self.turnover else None
        }


@dataclass
class MarketEventData:
    """市场事件数据"""
    event_type: MarketEvent
    symbol: str
    timestamp: datetime
    data: Dict[str, Any]
    severity: str = "normal"  # normal, warning, critical
    message: str = ""
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            'event_type': self.event_type.value,
            'symbol': self.symbol,
            'timestamp': self.timestamp.isoformat(),
            'data': self.data,
            'severity': self.severity,
            'message': self.message
        }


@dataclass
class MonitorConfig:
    """监控配置"""
    symbols: List[str] = field(default_factory=list)
    update_interval: float = 1.0  # 更新间隔（秒）
    price_change_threshold: Decimal = Decimal('0.05')  # 价格变动阈值（5%）
    volume_spike_threshold: Decimal = Decimal('2.0')   # 成交量异常阈值（2倍）
    volatility_threshold: Decimal = Decimal('0.02')    # 波动率阈值（2%）
    enable_circuit_breaker: bool = True
    enable_volume_monitoring: bool = True
    enable_volatility_monitoring: bool = True
    max_data_points: int = 1000  # 最大数据点数


class MarketMonitor:
    """市场监控器"""
    
    def __init__(self, config: Optional[MonitorConfig] = None):
        self.config = config or MonitorConfig()
        self.logger = logging.getLogger(__name__)
        
        # 数据存储
        self.market_data: Dict[str, List[MarketData]] = {}
        self.market_status: Dict[str, MarketStatus] = {}
        self.last_prices: Dict[str, Decimal] = {}
        self.volume_history: Dict[str, List[int]] = {}
        
        # 事件处理
        self.event_handlers: Dict[MarketEvent, List[Callable]] = {
            event: [] for event in MarketEvent
        }
        self.event_queue: asyncio.Queue = asyncio.Queue()
        
        # 运行状态
        self.is_running = False
        self.monitor_task: Optional[asyncio.Task] = None
        self._lock = asyncio.Lock()
    
    async def start(self):
        """启动监控"""
        if self.is_running:
            return
        
        self.is_running = True
        self.monitor_task = asyncio.create_task(self._monitor_loop())
        self.logger.info("市场监控器已启动")
    
    async def stop(self):
        """停止监控"""
        if not self.is_running:
            return
        
        self.is_running = False
        if self.monitor_task:
            self.monitor_task.cancel()
            try:
                await self.monitor_task
            except asyncio.CancelledError:
                pass
        
        self.logger.info("市场监控器已停止")
    
    async def add_symbol(self, symbol: str):
        """添加监控标的"""
        async with self._lock:
            if symbol not in self.config.symbols:
                self.config.symbols.append(symbol)
                self.market_data[symbol] = []
                self.volume_history[symbol] = []
                self.logger.info(f"添加监控标的: {symbol}")
    
    async def remove_symbol(self, symbol: str):
        """移除监控标的"""
        async with self._lock:
            if symbol in self.config.symbols:
                self.config.symbols.remove(symbol)
                self.market_data.pop(symbol, None)
                self.volume_history.pop(symbol, None)
                self.last_prices.pop(symbol, None)
                self.market_status.pop(symbol, None)
                self.logger.info(f"移除监控标的: {symbol}")
    
    async def update_market_data(self, data: MarketData):
        """更新市场数据"""
        async with self._lock:
            symbol = data.symbol
            
            # 存储数据
            if symbol not in self.market_data:
                self.market_data[symbol] = []
            
            self.market_data[symbol].append(data)
            
            # 限制数据点数
            if len(self.market_data[symbol]) > self.config.max_data_points:
                self.market_data[symbol] = self.market_data[symbol][-self.config.max_data_points:]
            
            # 更新价格历史
            self.last_prices[symbol] = data.price
            
            # 更新成交量历史
            if symbol not in self.volume_history:
                self.volume_history[symbol] = []
            self.volume_history[symbol].append(data.volume)
            if len(self.volume_history[symbol]) > 100:  # 保留最近100个数据点
                self.volume_history[symbol] = self.volume_history[symbol][-100:]
            
            # 检测事件
            await self._detect_events(data)
    
    async def _detect_events(self, data: MarketData):
        """检测市场事件"""
        symbol = data.symbol
        
        # 价格变动检测
        if self.config.price_change_threshold and symbol in self.last_prices:
            if len(self.market_data[symbol]) >= 2:
                prev_data = self.market_data[symbol][-2]
                price_change = abs(data.price - prev_data.price) / prev_data.price
                
                if price_change >= self.config.price_change_threshold:
                    event = MarketEventData(
                        event_type=MarketEvent.PRICE_CHANGE,
                        symbol=symbol,
                        timestamp=data.timestamp,
                        data={
                            'current_price': str(data.price),
                            'previous_price': str(prev_data.price),
                            'change_rate': str(price_change),
                            'threshold': str(self.config.price_change_threshold)
                        },
                        severity="warning" if price_change < Decimal('0.1') else "critical",
                        message=f"{symbol} 价格变动 {price_change:.2%}"
                    )
                    await self._emit_event(event)
        
        # 成交量异常检测
        if (self.config.enable_volume_monitoring and 
            symbol in self.volume_history and 
            len(self.volume_history[symbol]) >= 10):
            
            recent_volumes = self.volume_history[symbol][-10:]
            avg_volume = sum(recent_volumes[:-1]) / len(recent_volumes[:-1])
            
            if avg_volume > 0:
                volume_ratio = data.volume / avg_volume
                
                if volume_ratio >= self.config.volume_spike_threshold:
                    event = MarketEventData(
                        event_type=MarketEvent.VOLUME_SPIKE,
                        symbol=symbol,
                        timestamp=data.timestamp,
                        data={
                            'current_volume': data.volume,
                            'average_volume': int(avg_volume),
                            'volume_ratio': str(volume_ratio),
                            'threshold': str(self.config.volume_spike_threshold)
                        },
                        severity="warning",
                        message=f"{symbol} 成交量异常 {volume_ratio:.1f}倍"
                    )
                    await self._emit_event(event)
        
        # 波动率检测
        if (self.config.enable_volatility_monitoring and 
            len(self.market_data[symbol]) >= 20):
            
            recent_prices = [d.price for d in self.market_data[symbol][-20:]]
            returns = []
            for i in range(1, len(recent_prices)):
                ret = (recent_prices[i] - recent_prices[i-1]) / recent_prices[i-1]
                returns.append(ret)
            
            if returns:
                # 计算标准差（波动率）
                mean_return = sum(returns) / len(returns)
                variance = sum((r - mean_return) ** 2 for r in returns) / len(returns)
                import math
                volatility = Decimal(str(math.sqrt(float(variance))))
                
                if volatility >= self.config.volatility_threshold:
                    event = MarketEventData(
                        event_type=MarketEvent.VOLATILITY_SPIKE,
                        symbol=symbol,
                        timestamp=data.timestamp,
                        data={
                            'volatility': str(volatility),
                            'threshold': str(self.config.volatility_threshold),
                            'period': 20
                        },
                        severity="warning",
                        message=f"{symbol} 波动率异常 {volatility:.2%}"
                    )
                    await self._emit_event(event)
    
    async def _emit_event(self, event: MarketEventData):
        """发送事件"""
        await self.event_queue.put(event)
        
        # 调用事件处理器
        handlers = self.event_handlers.get(event.event_type, [])
        for handler in handlers:
            try:
                if asyncio.iscoroutinefunction(handler):
                    await handler(event)
                else:
                    handler(event)
            except Exception as e:
                self.logger.error(f"事件处理器执行失败: {e}")
    
    def add_event_handler(self, event_type: MarketEvent, handler: Callable):
        """添加事件处理器"""
        if event_type not in self.event_handlers:
            self.event_handlers[event_type] = []
        self.event_handlers[event_type].append(handler)
    
    def remove_event_handler(self, event_type: MarketEvent, handler: Callable):
        """移除事件处理器"""
        if event_type in self.event_handlers:
            try:
                self.event_handlers[event_type].remove(handler)
            except ValueError:
                pass
    
    async def get_latest_data(self, symbol: str) -> Optional[MarketData]:
        """获取最新数据"""
        async with self._lock:
            if symbol in self.market_data and self.market_data[symbol]:
                return self.market_data[symbol][-1]
            return None
    
    async def get_historical_data(self, symbol: str, count: int = 100) -> List[MarketData]:
        """获取历史数据"""
        async with self._lock:
            if symbol in self.market_data:
                return self.market_data[symbol][-count:]
            return []
    
    async def get_market_status(self, symbol: str) -> Optional[MarketStatus]:
        """获取市场状态"""
        return self.market_status.get(symbol)
    
    async def set_market_status(self, symbol: str, status: MarketStatus):
        """设置市场状态"""
        async with self._lock:
            self.market_status[symbol] = status
            self.logger.info(f"{symbol} 市场状态: {status.value}")
    
    async def _monitor_loop(self):
        """监控循环"""
        while self.is_running:
            try:
                # 处理事件队列
                while not self.event_queue.empty():
                    try:
                        event = await asyncio.wait_for(
                            self.event_queue.get(), 
                            timeout=0.1
                        )
                        self.logger.info(f"市场事件: {event.message}")
                    except asyncio.TimeoutError:
                        break
                
                # 检查市场状态
                await self._check_market_status()
                
                await asyncio.sleep(self.config.update_interval)
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"监控循环错误: {e}")
                await asyncio.sleep(1)
    
    async def _check_market_status(self):
        """检查市场状态"""
        now = datetime.now()
        
        # 简单的市场时间检查（可根据实际需要扩展）
        if now.weekday() >= 5:  # 周末
            status = MarketStatus.CLOSED
        elif now.hour < 9:  # 9点前
            status = MarketStatus.CLOSED
        elif 9 <= now.hour < 9 or (now.hour == 9 and now.minute < 30):  # 9:00-9:30
            status = MarketStatus.PRE_OPEN
        elif (9 <= now.hour < 11 and now.minute >= 30) or (13 <= now.hour < 15):  # 交易时间
            status = MarketStatus.OPEN
        elif 11 <= now.hour < 13:  # 午休
            status = MarketStatus.BREAK
        elif now.hour >= 15:  # 15点后
            status = MarketStatus.AFTER_HOURS
        else:
            status = MarketStatus.CLOSED
        
        # 更新所有标的的市场状态
        for symbol in self.config.symbols:
            if symbol not in self.market_status or self.market_status[symbol] != status:
                await self.set_market_status(symbol, status)
    
    async def get_statistics(self) -> Dict[str, Any]:
        """获取监控统计信息"""
        async with self._lock:
            stats = {
                'monitored_symbols': len(self.config.symbols),
                'total_data_points': sum(len(data) for data in self.market_data.values()),
                'active_symbols': len([s for s in self.config.symbols if s in self.last_prices]),
                'market_status': {symbol: status.value for symbol, status in self.market_status.items()},
                'last_update': datetime.now().isoformat(),
                'is_running': self.is_running
            }
            
            # 添加每个标的的统计
            symbol_stats = {}
            for symbol in self.config.symbols:
                if symbol in self.market_data and self.market_data[symbol]:
                    latest = self.market_data[symbol][-1]
                    symbol_stats[symbol] = {
                        'latest_price': str(latest.price),
                        'latest_volume': latest.volume,
                        'data_points': len(self.market_data[symbol]),
                        'last_update': latest.timestamp.isoformat()
                    }
            
            stats['symbols'] = symbol_stats
            return stats